#include <db_pg.hpp>
#include <resultset_pg.hpp>
#include <cstring>

namespace iotplat {

    CDbPg::CDbPg(const char *dbName, const char *host, short port, const EExtention &extention) : CDb(dbName, host,
                                                                                                      port),
                                                                                                  m_extention(
                                                                                                          extention) {
        m_con = NULL;
    }

    CDbPg::~CDbPg() {
        if (m_con != NULL)
            PQfinish(m_con);
    }

    CDb::EDbType CDbPg::dbType() {
        if (m_extention == ExHgdb)
            return DtHgdb;

        return DtPg;
    }

    bool CDbPg::isConnected() {
        if (m_con != NULL) {
            switch (PQstatus(m_con)) {
                case CONNECTION_OK:
                    return true;

                case CONNECTION_STARTED:
                    //log().info(THISMODULE "等待进行连接");
                    break;
                case CONNECTION_MADE:
                    //log().info(THISMODULE "连接成功，等待发送");
                    break;
                case CONNECTION_AWAITING_RESPONSE:
                    //log().info(THISMODULE "等待来自服务器的响应");
                    break;
                case CONNECTION_AUTH_OK:
                    //log().info(THISMODULE "已收到认证；等待后端启动结束");
                    break;
                case CONNECTION_SSL_STARTUP:
                    //log().info(THISMODULE "协商SSL加密");
                    break;
                case CONNECTION_SETENV:
                    //log().info(THISMODULE "协商环境驱动的参数设置");
                    break;
                case CONNECTION_BAD:
                    //log().info(THISMODULE "链接故障");
                    break;
                default:
                    break;
                    //log().info(THISMODULE "未知原因");
            }
        }

        return false;
    }

    bool CDbPg::connect(const char *user, const char *pw) {
        if (m_con) {
            //log().debug(THISMODULE "释放连接");
            PQfinish(m_con);
        }

        if (m_port == -1)
            m_con = PQsetdbLogin(m_host.c_str(), NULL, NULL, NULL, m_dbName.c_str(), user, pw);
        else {
            std::string port = std::to_string(m_port);
            //log().debug(THISMODULE "连接数据库:%s@%s:%s",m_dbName.c_str(),m_host.c_str(),port.c_str());
            m_con = PQsetdbLogin(m_host.c_str(), port.c_str(), NULL, NULL, m_dbName.c_str(), user, pw);
            if (m_con)
                PQerrorMessage(m_con);
            else {
                //log().error(THISMODULE "连接失败:%s:%s",m_host.c_str(),m_port);
            }
        }
        return isConnected();
    }

    void CDbPg::disconnect() {
        if (m_con) {
            PQfinish(m_con);
            m_con = NULL;
        }
    }

    bool CDbPg::exec(const char *sql) {
        if (isConnected()) {
            PGresult *result = PQexec(m_con, sql);
            if (result) {
                if (PQresultStatus(result) == PGRES_COMMAND_OK) {
                    PQclear(result);
                    return true;
                }
                PQclear(result);
            }
        }

        return false;
    }

    int64_t CDbPg::execWithLastId(const char *sql) {
        if (isConnected()) {
            int len = strlen(sql);
            char *sql_x = new char[len + 20];
            std::sprintf(sql_x, "%s RETURNING ID", sql);
            PGresult *result = PQexec(m_con, sql_x);
            delete[] sql_x;

            if (result) {
                if (PQresultStatus(result) == PGRES_TUPLES_OK) {
                    int64_t rt = atol(PQgetvalue(result, 0, 0));
                    PQclear(result);
                    return rt;
                }
                PQclear(result);
            }
        }

        return -1;
    }

    CResultSet *CDbPg::query(const char *sql) {
        if (isConnected()) {
            CResultSetPg *rs = new CResultSetPg();

            rs->m_rst = PQexec(m_con, sql);
            if (rs->m_rst) {
                rs->m_r = -1;
                if (PQresultStatus(rs->m_rst) == PGRES_TUPLES_OK)
                    return rs;
            }

            delete rs;
        }

        return NULL;
    }

/**
 * 获取匹配字符串表名
 * @param pattern
 * @param rs
 * @return
 */
    CResultSet *CDbPg::getTables(const char *pattern) {
        std::string sql = "select table_name from information_schema.tables where table_type='BASE TABLE' and table_name like '";
        sql += pattern;
        sql += "'";

        return query(sql.c_str());
    }
}
